from itemadapter import ItemAdapter

# 以下是多个管道示例：集成到一个文件中，便于管理和维护。### ###


# 数据存为JSON文件（Python原生JSON序列化） 示例：
import json

class JsonSerializePipeline:
    # 重写以下方法体：数据处理逻辑 # 
    #【注意】：在Pipelines文件中，我们对方法是进行重写，以下三个方法名和形参名不能更改
    # （1）open_spider： 在爬虫执行前执行的方法。
    # （2）process_item：在爬虫执行过程中，对item进行处理的方法。
    # （3）close_spider：在爬虫执行后执行的方法。

    # 开始
    def open_spider(self, spider):
        # 使用文本模式
        self.file = open("./logs/dushu_books.json", "w", encoding="utf-8")
    # 写入
    def process_item(self, item, spider):
        # 直接写入item对象，会报错：TypeError: Object of type 'BookItem' is not JSON serializable
        # self.file.write(str(item))
        
        # 解决方法：使用json.dumps()方法将item对象序列化为JSON字符串，然后写入文件。
        # 注意：json.dumps()方法默认会将所有非ASCII字符进行转义，因此需要设置ensure_ascii=False来禁用此功能。
        # 1、使用json.dumps()方法将item对象序列化为JSON字符串，然后写入文件。
        # self.file.write(json.dumps(dict(item), ensure_ascii=False) + "\n")
        # 2、使用ItemAdapter类将item对象转换为字典，然后序列化。
        # self.file.write(json.dumps(ItemAdapter(item).asdict(), ensure_ascii=False) + "\n")

        # 或者 Item 转换为字典并序列化为 JSON 字符串；建议保留缩进格式（indent=2）以提高可读性
        self.file.write(json.dumps(ItemAdapter(item).asdict(), ensure_ascii=False, indent=2) + "\n")
        
        return item
    # 结束
    def close_spider(self, spider):
        self.file.close()

# #####################################################################################################################
# #####################################################################################################################


# 数据存为JSON文件（JsonItemExporter 自动处理编码 --- 推荐） 示例：
from scrapy.exporters import JsonItemExporter

class JsonPipeline:
    # 重写以下方法体：数据处理逻辑 # 
    #【注意】：在Pipelines文件中，我们对方法是进行重写，以下三个方法名和形参名不能更改
    # （1）open_spider： 在爬虫执行前执行的方法。
    # （2）process_item：在爬虫执行过程中，对item进行处理的方法。
    # （3）close_spider：在爬虫执行后执行的方法。

    # 开始
    def open_spider(self,spider):

        # 核心说明："wb" 表示以二进制模式写入文件，但 encoding 参数仅适用于文本模式（如 "w" 或 "r"）。

        # 使用二进制模式 + JsonItemExporter 自动处理编码（JsonItemExporter 默认必须要求文件以二进制模式打开（"wb"），
        # 否则当管道以文本模式（"w"）打开文件会导致 bytes 数据无法写入文本流。）
        self.file = open("./logs/dushu_books.json", "wb")        
        self.exporter = JsonItemExporter(self.file, ensure_ascii=False, encoding="utf-8")
        self.exporter.start_exporting()
    # 写入
    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item
    # 结束
    def close_spider(self,spider):
        self.exporter.finish_exporting()
        self.file.close()

# #####################################################################################################################
# #####################################################################################################################


# 数据入MySQL数据库 示例：
from scrapy.utils.project import get_project_settings   # 加载settings文件
import pymysql

# 数据库
class MysqlPipeline:
    # 开始
    def open_spider(self, spider):
        settings = get_project_settings()
        self.host = settings['MYSQLDB_HOST']
        self.port = settings['MYSQLDB_PORT']
        self.user = settings['MYSQLDB_USER']
        self.password = settings['MYSQLDB_PASSWORD']
        self.name = settings['MYSQLDB_NAME']
        self.charset = settings['MYSQLDB_CHARSET']

        self.connect()
    def connect(self):
        self.conn = pymysql.connect(
                            host=self.host,
                            port=self.port,
                            user=self.user,
                            password=self.password,
                            db=self.name,
                            charset=self.charset
        )
        # 游标
        self.cursor = self.conn.cursor()

    # 写入
    def process_item(self, item, spider):
        # sql
        sql = 'insert into book(name,src) values ("{}","{}")'.format(item['bknm'],item['src'])
        
        try:
            self.cursor.execute(sql)
            self.conn.commit()
        except pymysql.Error as ex:
            self.conn.rollback()
            spider.logger.error(f"Database error: {ex}")
            
        return item

    # 结束
    def close_spider(self, spider):
        # 关闭
        self.cursor.close()
        self.conn.close()